/**
 * Copyright (c) 2006-2015, JGraph Ltd
 * Copyright (c) 2006-2015, Gaudenz Alder
 */
mxCodecRegistry.register(function () {
  /**
	 * Class: mxGraphViewCodec
	 *
	 * Custom encoder for <mxGraphView>s. This class is created
	 * and registered dynamically at load time and used implicitely via
	 * <mxCodec> and the <mxCodecRegistry>. This codec only writes views
	 * into a XML format that can be used to create an image for
	 * the graph, that is, it contains absolute coordinates with
	 * computed perimeters, edge styles and cell styles.
	 */
  var codec = new mxObjectCodec(new mxGraphView())

  /**
	 * Function: encode
	 *
	 * Encodes the given <mxGraphView> using <encodeCell>
	 * starting at the model's root. This returns the
	 * top-level graph node of the recursive encoding.
	 */
  codec.encode = function (enc, view) {
    return this.encodeCell(enc, view,
      view.graph.getModel().getRoot())
  }

  /**
	 * Function: encodeCell
	 *
	 * Recursively encodes the specifed cell. Uses layer
	 * as the default nodename. If the cell's parent is
	 * null, then graph is used for the nodename. If
	 * <mxGraphModel.isEdge> returns true for the cell,
	 * then edge is used for the nodename, else if
	 * <mxGraphModel.isVertex> returns true for the cell,
	 * then vertex is used for the nodename.
	 *
	 * <mxGraph.getLabel> is used to create the label
	 * attribute for the cell. For graph nodes and vertices
	 * the bounds are encoded into x, y, width and height.
	 * For edges the points are encoded into a points
	 * attribute as a space-separated list of comma-separated
	 * coordinate pairs (eg. x0,y0 x1,y1 ... xn,yn). All
	 * values from the cell style are added as attribute
	 * values to the node.
	 */
  codec.encodeCell = function (enc, view, cell) {
    var model = view.graph.getModel()
    var state = view.getState(cell)
    var parent = model.getParent(cell)

    if (parent == null || state != null) {
      var childCount = model.getChildCount(cell)
      var geo = view.graph.getCellGeometry(cell)
      var name = null

      if (parent == model.getRoot()) {
        name = 'layer'
      } else if (parent == null) {
        name = 'graph'
      } else if (model.isEdge(cell)) {
        name = 'edge'
      } else if (childCount > 0 && geo != null) {
        name = 'group'
      } else if (model.isVertex(cell)) {
        name = 'vertex'
      }

      if (name != null) {
        var node = enc.document.createElement(name)
        var lab = view.graph.getLabel(cell)

        if (lab != null) {
          node.setAttribute('label', view.graph.getLabel(cell))

          if (view.graph.isHtmlLabel(cell)) {
            node.setAttribute('html', true)
          }
        }

        if (parent == null) {
          var bounds = view.getGraphBounds()

          if (bounds != null) {
            node.setAttribute('x', Math.round(bounds.x))
            node.setAttribute('y', Math.round(bounds.y))
            node.setAttribute('width', Math.round(bounds.width))
            node.setAttribute('height', Math.round(bounds.height))
          }

          node.setAttribute('scale', view.scale)
        } else if (state != null && geo != null) {
          // Writes each key, value in the style pair to an attribute
				    for (var i in state.style) {
				    	var value = state.style[i]

				    	// Tries to turn objects and functions into strings
					    if (typeof (value) === 'function' &&
							typeof (value) === 'object') {
					    	value = mxStyleRegistry.getName(value)
				        }

				    	if (value != null &&
				    		typeof (value) !== 'function' &&
							typeof (value) !== 'object') {
              node.setAttribute(i, value)
				        }
				    }

          var abs = state.absolutePoints

          // Writes the list of points into one attribute
          if (abs != null && abs.length > 0) {
            var pts = Math.round(abs[0].x) + ',' + Math.round(abs[0].y)

            for (var i = 1; i < abs.length; i++) {
              pts += ' ' + Math.round(abs[i].x) + ',' +
								Math.round(abs[i].y)
            }

            node.setAttribute('points', pts)
          }

          // Writes the bounds into 4 attributes
          else {
            node.setAttribute('x', Math.round(state.x))
            node.setAttribute('y', Math.round(state.y))
            node.setAttribute('width', Math.round(state.width))
            node.setAttribute('height', Math.round(state.height))
          }

          var offset = state.absoluteOffset

          // Writes the offset into 2 attributes
          if (offset != null) {
            if (offset.x != 0) {
              node.setAttribute('dx', Math.round(offset.x))
            }

            if (offset.y != 0) {
              node.setAttribute('dy', Math.round(offset.y))
            }
          }
        }

        for (var i = 0; i < childCount; i++) {
          var childNode = this.encodeCell(enc,
            view, model.getChildAt(cell, i))

          if (childNode != null) {
            node.appendChild(childNode)
          }
        }
      }
    }

    return node
  }

  // Returns the codec into the registry
  return codec
}())
